/*
Copyright 2010 Thomas W. Holtquist

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

using System;
using System.Collections;
using System.Threading;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using Microsoft.SPOT.IO;

using GHIElectronics.NETMF.IO;
using GHIElectronics.NETMF.USBHost;

namespace Skewworks.Pyxis.Kernel
{

    [Serializable]
    public delegate void OnDriveAdded(string Root);

    [Serializable]
    public delegate void OnDriveRemoved(string Root);

    public class DriveManager : MarshalByRefObject
    {

        #region Variables

        private ArrayList _drives = new ArrayList();
        private PersistentStorage _sd;
        private Thread _sdWatcher = null;
        private string _root = "\\";                     // Path to Root of Primary HDD

        private PyxisAPI API;

        #endregion

        #region Constructor

        public DriveManager(PyxisAPI api)
        {
            API = api;

            if (api.ActiveDevice == PyxisAPI.DeviceType.Emulator) return;

            // Subscribe to RemovableMedia events
            RemovableMedia.Insert += RemovableMedia_Insert;
            RemovableMedia.Eject += RemovableMedia_Eject;

            // Handle SD using best case method
            MountSD();

        }

        #endregion

        #region Events

        public event OnDriveAdded DriveAdded;

        /// <summary>
        /// Event for Taps
        /// </summary>
        /// <param name="sender"></param>
        protected virtual void OnDriveAdded(string Root)
        {
            if (DriveAdded != null) DriveAdded(Root);
        }

        public event OnDriveRemoved DriveRemoved;

        /// <summary>
        /// Event for Taps
        /// </summary>
        /// <param name="sender"></param>
        protected virtual void OnDriveRemoved(string Root)
        {
            if (DriveRemoved != null) DriveRemoved(Root);
        }
        #endregion

        #region Properties

        public PyxisDrive[] AvailableDrives
        {
            get
            {
                PyxisDrive[] pds = new PyxisDrive[_drives.Count];
                for (int i = 0; i < _drives.Count; i++)
                {
                    pds[i] = (PyxisDrive)_drives[i];
                }
                return pds;
            }
        }

        public string[] DriveRoots
        {
            get
            {
                string[] pds = new string[_drives.Count];
                PyxisDrive pd;
                for (int i = 0; i < _drives.Count; i++)
                {
                    pd = (PyxisDrive)_drives[i];
                    pds[i] = pd.RootName;
                }
                return pds;
            }
        }

        public string RootDirectory
        {
            get { return _root; }
            internal set { _root = value; }
        }

        public bool SDMounted
        {
            get
            {
                if (_sd == null) return false;
                return true;
            }
        }

        #endregion

        #region Public Methids

        public void FlushFileSystems()
        {
            for (int i = 0; i < _drives.Count; i++)
            {
                PyxisDrive pd = (PyxisDrive)_drives[i];
                pd.VolumeInfo.FlushAll();
            }
        }

        public bool MountSD()
        {
            try
            {
                _sd = new PersistentStorage("SD");
                _sd.MountFileSystem();

                if (API.ActiveDevice == PyxisAPI.DeviceType.Cobra)
                {
                    _sdWatcher = new Thread(WatchMountedSDCard);
                    _sdWatcher.Priority = ThreadPriority.AboveNormal;
                    _sdWatcher.Start();
                }

                return true;
            }
            catch (Exception)
            {
                if (API.ActiveDevice == PyxisAPI.DeviceType.Cobra)
                {
                    StartSDWatcher();
                }
                return false;
            }
        }

        public void UnmountSD()
        {
            try
            {
                _sd.UnmountFileSystem();
            }
            catch (Exception)
            {
            }
            _sd = null;
        }

        #endregion

        #region Internal Methods

        internal void AddUSBDrive(USBH_Device device)
        {
            PyxisDrive pd = new PyxisDrive();       // Create the Drive object
            pd.ps = new PersistentStorage(device);  // Create an PersistantStorage for the device
            pd.Device = device;                     // Assign Device to the Drive

            try
            {
                pd.ps.MountFileSystem();                // Mount the FileSystem
            }
            catch (Exception e)
            {
                Debug.Print("couldn't mount!\n" + e.Message);
                return;
            }
            _drives.Add(pd);                        // Add drive to Array

        }

        #endregion

        #region Private Methods

        private void RemovableMedia_Insert(object sender, MediaEventArgs e)
        {
            // Because of how this is called we have no choice
            // But to assume this is always the last mounted PS
            if (e.Volume.RootDirectory != "\\SD")
            {
                try
                {
                    PyxisDrive pd = (PyxisDrive)_drives[_drives.Count - 1];
                    pd.Formatted = e.Volume.IsFormatted;
                    pd.RootName = e.Volume.RootDirectory;
                    pd.VolumeName = e.Volume.Name;
                    pd.VolumeInfo = e.Volume;
                    _drives[_drives.Count - 1] = pd;
                }
                catch (Exception) { }
            }
            else
            {
                PyxisDrive pd = new PyxisDrive();
                pd.Formatted = e.Volume.IsFormatted;
                pd.RootName = e.Volume.RootDirectory;
                pd.VolumeName = e.Volume.Name;
                pd.VolumeInfo = e.Volume;
                pd.ps = _sd;
                _drives.Add(pd);
            }

            OnDriveAdded(e.Volume.RootDirectory);
        }

        private void RemovableMedia_Eject(object sender, MediaEventArgs e)
        {
            PyxisDrive pd;
            for (int i = 0; i < _drives.Count; i++)
            {
                pd = (PyxisDrive)_drives[i];
                if (pd.RootName == e.Volume.RootDirectory)
                {
                    _drives.Remove(pd);
                    if (pd.RootName == "\\SD")
                    {
                        if (API.ActiveDevice == PyxisAPI.DeviceType.Cobra)
                        {
                            // Watch for the card
                            _sdWatcher = new Thread(WatchSDCard);
                            _sdWatcher.Priority = ThreadPriority.AboveNormal;
                            _sdWatcher.Start();
                        }
                    }
                    OnDriveRemoved(e.Volume.RootDirectory);
                    return;
                }
            }

        }

        private void StartSDWatcher()
        {
            // Watch for the card
            switch (API.ActiveDevice)
            {
                case PyxisAPI.DeviceType.Cobra:
                    _sdWatcher = new Thread(WatchSDCard);
                    _sdWatcher.Priority = ThreadPriority.AboveNormal;
                    _sdWatcher.Start();
                    break;
            }
        }

        private void WatchSDCard()
        {
            while (true)
            {
                if (PersistentStorage.DetectSDCard())
                {
                    try
                    {
                        _sd = new PersistentStorage("SD");
                        _sd.MountFileSystem();
                        return;
                    }
                    catch (Exception)
                    {
                        // Do nothing
                    }
                }
                Thread.Sleep(1000);
            }
        }

        private void WatchMountedSDCard()
        {
            while (true)
            {
                Thread.Sleep(1000);
                if (!PersistentStorage.DetectSDCard())
                {
                    UnmountSD();
                    return;
                }
            }
        }

        #endregion

    }

}
